#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include <error.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/md5.h>
#include <openssl/ssl.h>

//gcc rsa.c -lssl -l crypto

int main(int argc,char *argv[])
{
    int ret = -1;
    RSA *r;
    BIGNUM *e;

    FILE *fp;
    char *name,*header;
    unsigned char *data;
    long len;

    //初始化openssl库相关
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    OpenSSL_add_all_ciphers();
    OpenSSL_add_all_digests();

    fp = fopen("CA.pem","r");
    if(!fp)
    {
        perror("fopen ");
    }
    ret = PEM_read(fp,&name,&header,&data,&len);
    if(ret != 1)
    {
        printf("PEM_read error code:%08lx\r\n",ERR_get_error());
    }
    printf("name:%s,header:%s\r\n",name,header);
    printf("len:%ld,data:\r\n",len);
    for(long i = 0;i < len;i++)
    {
        if(i && (!(i % 10)))
        {
            printf("\r\n");
        }
        printf("%02x ",data[i]);
    }
    printf("\r\n");
    OPENSSL_free(name);
    OPENSSL_free(header);
    // OPENSSL_free(data);
    fclose(fp);

    fp = fopen("test.pem","w");
    if(!fp)
    {
        perror("fopen ");
    }
    ret = PEM_write(fp,"test","hello",data,len);
    if(ret < 0)
    {
        printf("PEM_write error code:%08lx\r\n",ERR_get_error());
    }
    OPENSSL_free(data);
    fclose(fp);
/////////////////////////////////////////////////////////////////////////

    e = BN_new();
    if(!e)
    {
        printf("BN_new error code:%08lx\r\n",ERR_get_error());
        return ret;
    }
    BN_init(e);
    //设置加密指数
    BN_set_word(e,RSA_F4);
    r = RSA_new();
    if(!r)
    {
        printf("RSA_new error code:%08lx\r\n",ERR_get_error());
        goto bn_free;
    }
    //生成密钥对
    ret = RSA_generate_key_ex(r,2048,e,NULL);
    if(ret <= 0)
    {
        printf("RSA_generate_key_ex error code:%08lx\r\n",ERR_get_error());
        goto rsa_free;
    }
    // RSA_print_fp(stdout,r,0);
/////////////////////////////////////////////////////////////////////////
    fp = fopen("CA.pem","r");
    if(!fp)
    {
        perror("fopen");
        return ret;
    }
    uint8_t *rd_buf;
    struct stat buf;
    fstat(fileno(fp),&buf);
    rd_buf = (uint8_t *)calloc(1,buf.st_size);
    len = fread(rd_buf,1,buf.st_size,fp);
    if(len != buf.st_size)
    {
        if(ferror(fp))
        {
            clearerr(fp);
            printf("fread error..\r\n");
        }
    }
    fclose(fp);
    unsigned char *sigret;
    unsigned int siglen;
    unsigned char md[16];
    memset(md,0,sizeof(md));
    //对数据进行摘要
    MD5((const uint8_t *)rd_buf,buf.st_size,md);
    sigret = (unsigned char *)calloc(1,RSA_size(r));
    //对摘要进行签名
    ret = RSA_sign(NID_md5,md,sizeof(md),sigret,&siglen,r);
    if(ret != 1)
    {
        printf("RSA_sign error code:%08lx\r\n",ERR_get_error());
    }
    printf("signature:\r\n\t");
    for(int32_t i = 0;i < siglen;i++)
    {
        if(i && !(i % 10))
        {
            printf("\r\n\t");
        }
        printf("%02x ",sigret[i]);
    }
    printf("\r\n");
    //对数据进行摘要
    MD5((const uint8_t *)rd_buf,buf.st_size,md);
    //对数据进行验签
    ret = RSA_verify(NID_md5,md,sizeof(md),sigret,siglen,r);
    if(ret != 1)
    {
        printf("RSA_verify error code:%08lx\r\n",ERR_get_error());
    }
    else
    {
        printf("RSA_verify ok..\r\n");
    }
    free(sigret);
/////////////////////////////////////////////////////////////////////////
    RSA *key;

    fp = fopen("PUBKEY.pem","w");
    if(!fp)
    {
        perror("fopen ");
    }
    ret = PEM_write_RSA_PUBKEY(fp,r);
    if(ret != 1)
    {
        printf("PEM_write_RSA_PUBKEY error code:%08lx\r\n",ERR_get_error());
    }
    fclose(fp);

    fp = fopen("PUBKEY.pem","r");
    if(!fp)
    {
        perror("fopen ");
    }
    key = PEM_read_RSA_PUBKEY(fp,NULL,NULL,NULL);
    if(!key)
    {
        printf("PEM_read_PUBKEY error code:%08lx\r\n",ERR_get_error());
    }
    fclose(fp);
    RSA_free(key);

    fp = fopen("PRIVATE.pem","w");
    if(!fp)
    {
        perror("fopen ");
    }
    //使用密码"hello"通过des ede3 cbc加密私钥
    ret = PEM_write_RSAPrivateKey(fp,r,EVP_des_ede3_cbc(),NULL,0,NULL,"hello");
    if(ret != 1)
    {
        printf("PEM_write_RSAPrivateKey error code:%08lx\r\n",ERR_get_error());
    }
    fclose(fp);

    fp = fopen("PRIVATE.pem","r");
    if(!fp)
    {
        perror("fopen ");
    }
    //使用密码"hello"读取私钥
    key = PEM_read_RSAPrivateKey(fp,NULL,NULL,"hello");
    if(!key)
    {
        // printf("%s\r\n",ERR_error_string(ERR_get_error(),NULL));
        printf("PEM_read_RSAPrivateKey error code:%08lx\r\n",ERR_get_error());
    }
    fclose(fp);
    RSA_free(key);
/////////////////////////////////////////////////////////////////////////
    char pub_from[] = {"hello word..\r\n"},*pub_to;
    //申请密文存放的内存
    pub_to = (char *)malloc(RSA_size(r));
    //公钥加密
    ret = RSA_public_encrypt(strlen(pub_from),pub_from,pub_to,r,RSA_PKCS1_OAEP_PADDING);
    if(ret < 0)
    {
        printf("RSA_public_encrypt error code:%08lx\r\n",ERR_get_error());
        goto rsa_free;
    }
    printf("plaintext:\r\n\t%s\r\n",pub_from);
    printf("cihpertext:\r\n\t");
    for(int32_t i = 0;i < RSA_size(r);i++)
    {
        if(i && !(i % 10))
        {
            printf("\r\n\t");
        }
        printf("%02x ",(uint8_t)pub_to[i]);
    }
    printf("\r\n");

    char *pri_from,pri_to[32] = {0};
    pri_from = pub_to;
    //私钥解密
    ret = RSA_private_decrypt(RSA_size(r),pri_from,pri_to,r,RSA_PKCS1_OAEP_PADDING);
    if(ret < 0)
    {
        printf("RSA_private_decrypt error code:%08lx\r\n",ERR_get_error());
        goto rsa_free;
    }
    printf("palintext:\r\n\t%s\r\n",pri_to);

    ret = 1;
rsa_free:
    //释放资源
    RSA_free(r);
bn_free:
    BN_clear_free(e);

    return ret;
}
